package ctp.statesgraph;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import ctp.ctpgraph.Edge;

public class Observation implements Arc {
	
	Map<Edge,EdgeStatus> edgesStates;
	double prob;
	
	/**
	 * Creates all possible observations from a given set of edges.
	 * 
	 * @param edges
	 * @return
	 */
	public static Set<Observation> buildObservations(Set<Edge> edges) {
		Set<Observation> observations = new HashSet<Observation>();
		
		if (edges.isEmpty()) {
			return observations;
		} 
			
		Edge firstEdge = edges.iterator().next();
		edges.remove(firstEdge);
		
		// Getting the observations of the rest of the edges.
		Set<Observation> restObservation = buildObservations(edges);
		
		if (restObservation.isEmpty()) {
			
			for (EdgeStatus edgeState : EdgeStatus.values()) {
				Map<Edge,EdgeStatus> edgesStates = new HashMap<Edge, EdgeStatus>();
				edgesStates.put(firstEdge, edgeState);
				double prob = -1.0;
				if (edgeState == EdgeStatus.BLOCKED) {
					prob = firstEdge.getProbBlocked();
				} else if (edgeState == EdgeStatus.OPEN){
					prob = 1 - firstEdge.getProbBlocked();
				} 
				Observation obs = new Observation(edgesStates, prob);
				observations.add(obs);
			}
			
		} else {
			for (Observation obs : restObservation) {
				Observation newObs = new Observation(obs);
				Map<Edge,EdgeStatus> edgesStates;
				
				edgesStates = obs.edgesStates;
				edgesStates.put(firstEdge, EdgeStatus.BLOCKED);
				obs.setProb(obs.getProb() * firstEdge.getProbBlocked());
				observations.add(obs);
				
				edgesStates = newObs.edgesStates;
				edgesStates.put(firstEdge, EdgeStatus.OPEN);
				newObs.setProb(newObs.getProb() * (1 - firstEdge.getProbBlocked()));			
				observations.add(newObs);
			}

		}

		return observations;
	}
	
	/**
	 * This constructor gets all the required fields.
	 * 
	 * @param edgesStates
	 * @param prod
	 */
	public Observation(Map<Edge, EdgeStatus> edgesStates, double prod) {
		super();
		this.edgesStates = edgesStates;
		this.prob = prod;
	}
	
	public Observation(Observation obs) {
		super();
		this.prob = obs.prob;
		this.edgesStates = new HashMap<Edge, EdgeStatus>();
		for (Entry<Edge, EdgeStatus> entry : obs.edgesStates.entrySet()) {
			this.edgesStates.put(entry.getKey(), entry.getValue());
		}
	}

	public double getProb() {
		return prob;
	}

	public void setProb(double prod) {
		this.prob = prod;
	}
	
	@Override
	public String toString() {
		return edgesStates.toString() + ":" + prob;
	}

	public Map<Edge, EdgeStatus> getEdgesStates() {
		return edgesStates;
	}

}
